﻿/// ****************************************************************************
/// Author:	        Robin Zhu
/// Create Date:	2015-11-25
/// Purpose: 		表示IP(v4)的子网的类，用于判断某个IP地址是否在子网内
/// ****************************************************************************
/// Modify By		Date			Remark
/// ****************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace RB.Utility
{
    public class IPSubnet
    {
        private ulong _netaddress;
        private ulong _mask;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ipSubnetString">like 192.168.1.0/24</param>
        public IPSubnet(string ipSubnetString)
        {
            ParseSubnetString(ipSubnetString);
        }

        private void ParseSubnetString(string ipSubnetString)
        {
            string ipstr, maskstr;
            if (RegexHelper.ExtractIPSubnet(ipSubnetString, out ipstr, out maskstr))
            {
                var ips = ipstr.Split('.');

                _netaddress = 0;
                for (int i=0; i<4; i++)
                {
                    _netaddress <<= 8;
                    ulong d = ulong.Parse(ips[i]);
                    if (d > 255)
                    {
                        throw new FormatException("Wrong IP address in IP subnet string.");
                    }
                    _netaddress += d;
                }
                int marksbits = int.Parse(maskstr);
                if (marksbits > 32)
                {
                    throw new FormatException("Wrong network mask in IP subnet string.");
                }

                _mask = 0;
                for (int i=0; i<32-marksbits;i++)
                {
                    _mask <<= 1;
                    _mask++;
                }
                _mask = ~_mask;
            }
            else
            {
                throw new FormatException("Wrong IP subnet string format.");
            }
        }

        public bool Contains(string ipString)
        {
            IPAddress ipaddress = null;
            if (IPAddress.TryParse(ipString, out ipaddress))
            {
                if (ipaddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    ipaddress = GetIP4Address(ipString);
                }
            }
            if (ipaddress != null)
            {
                ulong ip = 0;
                var ips = ipaddress.GetAddressBytes();
                for (int i = 0; i < 4; i++)
                {
                    ip <<= 8;
                    ulong d = ips[i];

                    ip += d;
                }
                return (ip & _mask) == (_netaddress & _mask);
            }
            else
            {
                throw new FormatException("Wrong IP address format.");
            }
        }

        public static IPAddress GetIP4Address(string ipString)
        {
            string IP4Address = String.Empty;

            foreach (IPAddress IPA in Dns.GetHostAddresses(ipString))
            {
                if (IPA.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    return IPA;
                }
            }
            foreach (IPAddress IPA in Dns.GetHostAddresses(Dns.GetHostName()))
            {
                if (IPA.AddressFamily.ToString() == "InterNetwork")
                {
                    return IPA;
                }
            }
            return null;
        }
    }
}
